import importlib
import traceback

from ctaf_core.ctaf_objects.base_exception import CTAFbaseException
from ctaf_core.ctaf_objects.base_ctaf_service import BaseCTAFservice
from typing import Any, Dict

from ctaf_core.ctaf_objects.base_service_request import BaseServiceReq
from ctaf_core.ctaf_objects.base_service_response import BaseServiceResp
from ctaf_core.ctaf_utils.config_process_util import load_local_service_sever_settings
from ctaf_core.log_trace_bundle.logging import LogRecorder, LoggerFactory
from ctaf_facade.enumerations.error_code_enum import ErrorCodeEnum
from ctaf_facade.enumerations.service_rsp_code_enum import ResponseCodeEnum


class LocalServiceContainer:
    log_recorder: LogRecorder = LoggerFactory.get_logger()

    #key:value of local_sevice_register_settings is "localServiceInfo":{"localServiceName01": {"servicePath": "localService01", "methodName": "serviceMethod01"},...}
    def __init__(self, local_sevice_register_settings: Dict[str, Dict[str, str]]):

        #key:value of this Dict is local_service_instance_name:local_service_instance
        self.__local_service_instances: Dict[str, BaseCTAFservice] = {}

        #key:value of this Dict is local_service_name:local_service_instance_name.service_method_name
        self.__registered_local_services: Dict[str, str] = {}

        self.register_local_services_from_settings(local_sevice_register_settings)


    def add_service_instance(self, instance_name: str, local_service_instance: BaseCTAFservice):
        self.__local_service_instances[instance_name] = local_service_instance


    def register_local_service(self, local_service_name: str, local_service_class_path: str, local_service_method_name: str):
        module_name, class_name = local_service_class_path.rsplit('.', 1)
        module = importlib.import_module(module_name)
        local_service_class = getattr(module, class_name)
        local_service_instance = local_service_class()
        self.add_service_instance(local_service_class_path, local_service_instance)
        self.__registered_local_services[local_service_name] = local_service_class_path + "." + local_service_method_name
        LocalServiceContainer.log_recorder.write_info_log(local_service_name + " has been locally registered: " + self.__registered_local_services[local_service_name])

    def is_registerd_local_service(self, local_service_name: str)->bool:
        return local_service_name in self.__registered_local_services.keys()
    def invoke_sevice(self, local_service_name: str, *args, **kwargs)-> Any:
        if  local_service_name in self.__registered_local_services.keys():
            local_service_method_path = self.__registered_local_services[local_service_name]
            local_service_class_path, local_service_method_name = local_service_method_path.rsplit('.', 1)
            local_service_instance = self.__local_service_instances[local_service_class_path]
            if not local_service_instance:
                raise CTAFbaseException(ErrorCodeEnum.NO_TARGET_SERVICE)
            if hasattr(local_service_instance, local_service_method_name):
                local_service_method = getattr(local_service_instance, local_service_method_name)
                return local_service_method(*args, **kwargs)
            else:
                raise CTAFbaseException(ErrorCodeEnum.NO_TARGET_SERVICE)
        else:
            raise CTAFbaseException(ErrorCodeEnum.NO_TARGET_SERVICE)

    def register_local_services_from_settings(self, local_sevice_register_settings: Dict[str, Dict[str, str]]):
        for local_service_name in local_sevice_register_settings.keys():
            local_service_class_path = local_sevice_register_settings[local_service_name]["servicePath"]
            local_service_method_name = local_sevice_register_settings[local_service_name]["methodName"]
            self.register_local_service(local_service_name,local_service_class_path, local_service_method_name)

class LocalServiceCallUitl:
    __local_service_container = None

    @staticmethod
    def init_local_service_container(local_service_server_setting_filename: str) :
        local_service_server_setting = load_local_service_sever_settings(local_service_server_setting_filename)
        if not local_service_server_setting:
            return
        local_sevice_register_settings: Dict[str, Dict[str, str]] = local_service_server_setting.get("localServiceInfo")
        if LocalServiceCallUitl.__local_service_container is None:
            LocalServiceCallUitl.__local_service_container = LocalServiceContainer(local_sevice_register_settings)
    @staticmethod
    def get_local_service_container() -> LocalServiceContainer:
        if LocalServiceCallUitl.__local_service_container is None:
            raise CTAFbaseException(ErrorCodeEnum.NO_LOCAL_SERVICE_CONTAINER)
        return LocalServiceCallUitl.__local_service_container

    @staticmethod
    def invoke_local_service(request: BaseServiceReq)-> BaseServiceResp:
        local_service_name = request.get_called_service_name()
        rep: BaseServiceResp = None
        try:
            rep = LocalServiceCallUitl.get_local_service_container().invoke_sevice(local_service_name, request)
            rep.set_called_service_name(request.get_called_service_name())
            rep.set_ctaf_trace_id(request.get_ctaf_trace_id())
            rep.set_response_code(ResponseCodeEnum.SUCCESS_EXECUTE)
        except Exception as e:  # noqa
            err_msg: str = traceback.format_exc()
            LocalServiceCallUitl.get_local_service_container().log_recorder.write_info_log(
                f"Locally execute service [{request.get_called_service_name()}] failed, trace id is [{request.get_ctaf_trace_id()}]：{err_msg}")
            rep: BaseServiceResp = BaseServiceResp()
            rep.set_called_service_name(request.get_called_service_name())
            rep.set_ctaf_trace_id(request.get_ctaf_trace_id())
            rep.set_response_code(ResponseCodeEnum.FAILED_EXECUTE)
        finally:
            return rep

    @staticmethod
    def is_local_service(request: BaseServiceReq)-> bool:
        return LocalServiceCallUitl.get_local_service_container().is_registerd_local_service(request.get_called_service_name())

